package cyce.server;

import java.io.Serializable;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import cyce.file.File;
import cyce.peer.IPeer;
import cyce.peer.IServerPeer;

/**
 * This ADT allows for a many-to-many association between
 * peers and files in the system. 
 * @author Eric
 */
public class PeerContentMap implements Serializable {	

	private static final long serialVersionUID = -9203580037251739598L;

	//Maintains the Peer to file mapping
	private Map<IServerPeer, List<File> > contentMap = new HashMap<IServerPeer, List<File> >();
	
	//Maintains the file to peer mapping
	private Map<File, List<IServerPeer> > peerMap = new HashMap<File, List<IServerPeer> >();
	
	//a list of all peers in the system, even those without content
	private List<IServerPeer> peers = new LinkedList<IServerPeer>();
	
	/**
	 * Adds a pair to the ADT
	 * @param peer The peer
	 * @param file The file
	 */
	public void addPair(IServerPeer peer, File file){
		
		List<File> cMap = contentMap.get(peer);
		if (cMap == null){
			cMap = new LinkedList<File>();
		}
		
		List<IServerPeer> pMap = peerMap.get(file);
		if (pMap == null){
			pMap = new LinkedList<IServerPeer>();
		}
		
		if (!pMap.contains(peer))
			pMap.add(peer);
		
		if (!cMap.contains(file))
			cMap.add(file);
		
		peerMap.put(file, pMap);
		
		contentMap.put(peer, cMap);
	}
	
	/**
	 * Remove a pair from the ADT
	 * @param peer The peer
	 * @param file The file
	 */
	public void removePair(IServerPeer peer, File file){
		
		List<File> cMap = contentMap.get(peer);
		if (cMap != null){
			if (cMap.contains(file))
				cMap.remove(file);
			
			if (cMap.size()==0){
				contentMap.remove(peer);
			} else {
				contentMap.put(peer, cMap);
			}
		}
		
		List<IServerPeer> pMap = peerMap.get(file);
		if (pMap != null){
			if (pMap.contains(peer))
				pMap.remove(peer);
			
			if (pMap.size()==0){
				peerMap.remove(file);
			} else {
				peerMap.put(file, pMap);
			}
		}	
	}
	
	/**
	 * Retrieve a list of all peers associated with a given file
	 * @param content The file
	 * @return The list of Peers
	 */
	public List<IPeer> getPeerList(File content){
		List<IServerPeer> keys = peerMap.get(content);
		List<IPeer> peers = new LinkedList<IPeer>();
		if (keys!=null){
			peers.addAll(keys);
		} 
		return peers;
	}
	
	/**
	 * Get a list of files offered by a given peer
	 * @param peer The peer
	 * @return The file list
	 */
	public List<File> getFileList(IServerPeer peer){
		List<File> retVal = contentMap.get(peer);
		if (retVal == null){
			return new LinkedList<File>();
		} else {
			return retVal;
		}
	}
	
	/**
	 * Add a peer and the files they offer to the ADT
	 * @param peer The peer
	 * @param files The files
	 */
	public void addPeer(IServerPeer peer, List<File> files){
		peers.add(peer);
		if (files!=null){
			for (File file : files){
				addPair(peer,file);
			}
		}
	}
	
	/**
	 * Remove a peer from the ADT
	 * @param peer
	 */
	public void removePeer(IServerPeer peer){
		peers.remove(peer);
		List<File> cMap = contentMap.get(peer);
		if (cMap != null){
			contentMap.remove(peer);
			
			for (File file : cMap){
				List<IServerPeer> pMap = peerMap.get(file);
				if (pMap != null){
					if (pMap.contains(peer))
						pMap.remove(peer);
					
					if (pMap.size()==0){
						peerMap.remove(file);
					} else {
						peerMap.put(file, pMap);
					}
				}
			}
		}
	}
	
	/**
	 * Remove a file from the ADT
	 * @param file The file
	 */
	public void removeFile(File file){
		List<IServerPeer> pMap = peerMap.get(file);
		if (pMap != null){
			peerMap.remove(file);
			
			for (IServerPeer peer: pMap){
				List<File> cMap = contentMap.get(peer);
				if (cMap != null){
					if (cMap.contains(file))
						cMap.remove(file);
					
					if (cMap.size()==0){
						contentMap.remove(peer);
					} else {
						contentMap.put(peer, cMap);
					}
				}
			}
		}
	}
	
	/**
	 * Retrieve a list of all peers in the system
	 * @return The peer list
	 */
	public List<IServerPeer> getPeers(){
		return peers;
	}
}
